<?php
/**
 * Handles database calls to the links database.
 * @copyright (c) 2012, University of Amsterdam
 * @author Athanasios Papapanagiotou <hello@thanpa.com>
 * @author Huy Hoang <hhoang.nl@gmail.com>
 * @author Khoi Anh	Ho <khoi.ho@tomtom.com>
 * @author Tuba Kaya Chomette <admin@tubakaya.com>
 */
class DAO_Links {
    /**
     * Holds the PDO object.
     * @var PDO
     */
    private $_db;
    /**
     * Constructs the DAO and initializes the db connection.
     * @return void.
     */
    public function __construct() {
        $this->_db = new PDO(
            'mysql:dbname=links;host=127.0.0.1',
            'root',
            ''
        );
    }
    /**
     * Save the provided links to the database.
     * @param array $links The links to save.
     * @return boolean True if the commits are saved.
     */
    public function save(array $links) {
        foreach ($links as $key => $link) {
            if (!(is_array($link['Objects']) || !empty($link['Objects']))) {
                throw new App_Runtime_Exception(
                    "Something is wrong with the objects in the link array in '{$key}'."
                );
            }
            if (empty($link['Action'])) {
                throw new App_Runtime_Exception(
                    "Something is wrong with the action in the link array in '{$key}'."
                );
            }
            if (!$this->_checkActionExists($link['Action'])) {
                $this->_insertAction($link['Action']);
            }
            foreach ($link['Objects'] as $type => $object) {
                if (!$this->_checkObjectExists($object, $type)) {
                    $this->_insertObject($object, $type);
                }
            }
            $objects = array_values($link['Objects']);
            $this->_insertAssocitation($objects[0], $objects[1], $link['Action']);
        }
    }
    /**
     * Retrieves the keyword associations from a specific developer.
     * @param string $developer The name of the developer.
     * @param array $keywords The keywords to include to the search.
     * @return array The result keywords.
     */
    public function getSpecificDeveloperKeywordAssociations($developer, array $keywords) {
        $keywordsStatement = "'".implode("','", $keywords)."'";
        $query = "
            select o.name, (
                select count(object_id)
                from associations a
                where a.association_object_id = o.id
                and object_id = (
                    select o.id from objects o
                    where name = '{$developer}'
                )
            ) associations
            from objects o
            where type = 'Keyword'
            and name in ({$keywordsStatement})
            order by associations desc;
        ";
        return $this->_db->query($query)->fetchAll();
    }
    /**
     * Retrieves the developer list order by how many associations with the keywords each developer
     * has.
     * @param array $keywords The keyword list to look for.
     * @param boolean $mostImportant Return only the most important developer.
     * @return array The list of developers.
     */
    public function getDevelopersForKeywords(array $keywords, $mostImportant = false) {
        $keywordsStatement = "'".implode("','", $keywords)."'";
        $query = "
            select o.name, (
                select count(association_object_id)
                from associations a
                where a.object_id = o.id
                and association_object_id IN (
                    select o.id from objects o
                    where name in ({$keywordsStatement})
                )
            ) associations
            from objects o
            where type = 'Author'
            order by associations desc
            limit 4
        ";
        if ($mostImportant) {
            $query .= "limit 1";
        }
        return $this->_db->query($query)->fetchAll();
    }
    /**
     * Retrieves the developer list sorted by importance.
     * @return array The list of developers.
     */
    public function getDevelopersByImportance() {
        $query = "
            select o.name, (
                select count(distinct association_object_id)
                from associations a
                where a.object_id = o.id
            ) associations
            from objects o
            where type = 'Author'
            order by associations desc;
        ";
        return $this->_db->query($query)->fetchAll();
    }
    /**
     * Checks if an action exists.
     * @param string $action The name of the action.
     * @return boolean If the action exists or not.
     */
    private function _checkActionExists($action)
    {
        return (bool)$this->_db->query("select id from actions where name = '{$action}'")
            ->rowCount(0);
    }
    /**
     * Inserts a new action in the database.
     * @param string $action The name of the action.
     * @return boolean If the insert is successful.
     */
    private function _insertAction($action)
    {
        return $this->_db->query("insert into actions (name) values ('{$action}')");
    }
    /**
     * Checks if an object exists.
     * @param string $object The name of the object.
     * @param string $type The type of the specified object.
     * @return boolean If the object exists or not.
     */
    private function _checkObjectExists($object, $type)
    {
        return (bool)$this->_db->query("select id from objects where name = '{$object}'")
        ->rowCount(0);
    }
    /**
     * Inserts a new object in the database.
     * @param string $object The name of the object.
     * @param string $type The type of the specified object.
     * @return boolean If the insert is successful.
     */
    private function _insertObject($object, $type)
    {
        return $this->_db->query(
            "insert into objects (name, type) values ('{$object}', '{$type}')"
        );
    }
    /**
     * Inserts a new association in the database.
     * @param string $object The object.
     * @param string $associatedObject The association object.
     * @param string $action The action to associate the two objects.
     * @return boolean If the insert is successful.
     */
    private function _insertAssocitation($object, $associatedObject, $action)
    {
        $actionId = $this->_db->query("select id from actions where name = '{$action}'")
            ->fetchColumn(0);
        $objectId = $this->_db->query("select id from objects where name = '{$object}'")
            ->fetchColumn(0);
        $associatedObjectId = $this->_db
            ->query("select id from objects where name = '{$associatedObject}'")
            ->fetchColumn(0);
        $query = "
            insert into associations (object_id, association_object_id, action_id)
            values ('{$objectId}', '{$associatedObjectId}', '{$actionId}')
        ";
        return (bool)$this->_db->query($query);
    }
}
